#include "ConfigValues.h"
#include "LogManager.h"
#include "Scheduler.h"
#include "DBlayer.h"
#include "DiffHLEngine.h"
#include "IniFile.h"

#include <curl/curl.h>
#include <libxslt/xsltInternals.h>

#include <windows.h>
#include <winsvc.h>
#include <direct.h>

//#define CRTDBG_MAP_ALLOC
//#include <crtdbg.h>

string config_db_host;
unsigned long config_db_port;
string config_db_name;
string config_db_username;
string config_db_password;
bool config_db_compress_html_resources;
bool config_db_compress_emails;

unsigned long config_cleaner_not_activated_account_period;
unsigned long config_cleaner_not_activated_monitor_period;
unsigned long config_cleaner_email_storage_period;

string config_emailsender_host;
unsigned long config_emailsender_port;
bool config_emailsender_auth;
string config_emailsender_username;
string config_emailsender_password;
unsigned long config_emailsender_threads_count;
string config_email_sms_sender; 

string config_downloader_default_http_user_agent;
string config_downloader_w3m_http_user_agent;
string config_downloader_temp_directory;
unsigned long config_downloader_threads_count;
unsigned long config_downloader_max_errors_count;
unsigned long config_downloader_max_download_size;
unsigned long config_downloader_max_favicon_size;
unsigned long config_downloader_max_redirs_count;

unsigned long config_siteproccesor_port;
unsigned long config_siteproccesor_max_clients_count;

string config_log_file;
unsigned long config_log_file_size;

SERVICE_STATUS          ssStatus;
SERVICE_STATUS_HANDLE   sshStatusHandle;
Scheduler*              pScheduler;
DBLayer*                dbLayer;

VOID WINAPI ServiceCtrl(DWORD dwCtrlCode);
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv);
BOOL WINAPI ControlHandler(DWORD dwCtrlType);

VOID ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
VOID ServiceStart(LPCTSTR szBinaryPathName);
VOID ServiceStop();

VOID CreateClientService(LPCTSTR szBinaryPathName);
VOID DeleteClientService();

static char* SERVICENAME         = "W3MonitorApp";
static char* SERVICEDISPLAYNAME  = "W3MonitorApp";

int main(int argc, char* argv[])
{
    if (argc == 2)
    {
        if (stricmp(argv[1], "/i") == 0)
        {
            CreateClientService(argv[0]);
            return 0;
        }
        if (stricmp(argv[1], "/u") == 0)
        {
            DeleteClientService();
            return 0;
        }
        if (stricmp(argv[1], "/v") == 0)
        {
            printf("W3M version 1.0 build %s %s\n", __DATE__, __TIME__);
        }
    }

#ifdef __SERVICE_MODE__
    SERVICE_TABLE_ENTRY dispatchTable[] =
    {
        { SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
        { NULL, NULL }
    };
    StartServiceCtrlDispatcher(dispatchTable);
#endif
#ifndef __SERVICE_MODE__
    ServiceStart(argv[0]);
#endif
	//_CrtDumpMemoryLeaks();
    return 0;
}

void WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
{
    char szModuleFileName[MAX_PATH];
    GetModuleFileName(NULL, szModuleFileName, MAX_PATH);
    sshStatusHandle = RegisterServiceCtrlHandler( SERVICENAME, ServiceCtrl);
    ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ssStatus.dwServiceSpecificExitCode = 0;
    ReportStatusToSCMgr( SERVICE_RUNNING, NO_ERROR, 0);    
    ServiceStart(szModuleFileName);
    if (sshStatusHandle)
        ReportStatusToSCMgr( SERVICE_STOPPED, NO_ERROR, 0);
    return;
}

VOID WINAPI ServiceCtrl(DWORD dwCtrlCode)
{
    switch(dwCtrlCode)
    {
    case SERVICE_CONTROL_STOP:
        ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
        ServiceStop();
        return;
    case SERVICE_CONTROL_SHUTDOWN:
        ServiceStop();
        return;
    case SERVICE_CONTROL_INTERROGATE:
        break;
    default:
        break;
    }
    ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
}

VOID ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
    static DWORD dwCheckPoint = 1;
    ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
    ssStatus.dwCurrentState = dwCurrentState;
    ssStatus.dwWin32ExitCode = dwWin32ExitCode;
    ssStatus.dwWaitHint = dwWaitHint;
    if ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED))
        ssStatus.dwCheckPoint = 0;
    else
        ssStatus.dwCheckPoint = dwCheckPoint++;
    SetServiceStatus(sshStatusHandle, &ssStatus);
}

VOID ServiceStart(LPCTSTR szBinaryPathName)
{
    string szFolder;
    char szDrive[3];
    char szDir[MAX_PATH];
    _splitpath(szBinaryPathName, szDrive, szDir, NULL, NULL);
    szFolder = szDrive;
    szFolder += szDir;
    _chdir(szFolder.c_str());

    IniFile iniFile(".\\w3m.ini");
    if (!iniFile.ReadFile())
        return;

    //DebugBreak();

    config_db_host = iniFile.GetValue("DB", "host");
    config_db_port = iniFile.GetValueI("DB", "port");
    config_db_name = iniFile.GetValue("DB", "db_name");
    config_db_username = iniFile.GetValue("DB", "username");
    config_db_password = iniFile.GetValue("DB", "password");
    config_db_compress_html_resources = iniFile.GetValueB("DB", "compress_html_resources");
    config_db_compress_emails = iniFile.GetValueB("DB", "compress_emails");

    config_cleaner_not_activated_account_period = iniFile.GetValueB("CLEANER", "not_activated_account_period");
    config_cleaner_not_activated_monitor_period = iniFile.GetValueB("CLEANER", "not_activated_monitor_period");
    config_cleaner_email_storage_period = iniFile.GetValueI("CLEANER", "email_storage_period");

    config_emailsender_host = iniFile.GetValue("EMAILSENDER", "host");
    config_emailsender_port = iniFile.GetValueI("EMAILSENDER", "port");
    config_emailsender_auth = iniFile.GetValueB("EMAILSENDER", "auth");
    config_emailsender_username = iniFile.GetValue("EMAILSENDER", "username");
    config_emailsender_password = iniFile.GetValue("EMAILSENDER", "password");
    config_emailsender_threads_count = iniFile.GetValueI("EMAILSENDER", "threads_count");    
	config_email_sms_sender = iniFile.GetValue("EMAILSENDER", "sms_sender_url");

    config_downloader_default_http_user_agent = iniFile.GetValue("DOWNLOADER", "default_http_user_agent");
    config_downloader_w3m_http_user_agent = iniFile.GetValue("DOWNLOADER", "w3m_http_user_agent");
    config_downloader_temp_directory = iniFile.GetValue("DOWNLOADER", "temp_directory");
    config_downloader_threads_count = iniFile.GetValueI("DOWNLOADER", "threads_count");    
    config_downloader_max_errors_count = iniFile.GetValueI("DOWNLOADER", "max_errors_count");
    config_downloader_max_download_size = iniFile.GetValueI("DOWNLOADER", "max_download_size");
    config_downloader_max_favicon_size = iniFile.GetValueI("DOWNLOADER", "max_favicon_size");
    config_downloader_max_redirs_count = iniFile.GetValueI("DOWNLOADER", "max_redirs_count");

    config_siteproccesor_port = iniFile.GetValueI("SITEPROCESSOR", "port");
    config_siteproccesor_max_clients_count = iniFile.GetValueI("SITEPROCESSOR", "max_clients_count");

    config_log_file = iniFile.GetValue("LOG", "log_file");
    config_log_file_size = iniFile.GetValueI("LOG", "log_file_size");

    logManager.SetLogFileMaxSize(config_log_file_size*1024*1024);
    logManager.SetLogFilePath(config_log_file);

    LOGT(("ServiceStart. W3M version 1.0 build %s %s started\n", __DATE__, __TIME__));

    LOGT(("ServiceStart. Set locale to: %s\n", setlocale (LC_CTYPE, "")));

    dbLayer = new DBLayer();
    LOGT(("ServiceStart. Connect to DB\n"));
    if (dbLayer->Connect(config_db_name, config_db_host, config_db_port, config_db_username, config_db_password))
        LOGT(("ServiceStart. Connection to DB completed\n"));

    dbLayer->ClearDiffCache(DiffHLEngine::GetVersion());

    CURLcode curlCode = curl_global_init(CURL_GLOBAL_ALL);
    if (curlCode != CURLE_OK)
        LOGT(("ServiceStart. CURL global init fail\n"));
    
    xmlInitParser();
    xsltInit();

    pScheduler = new Scheduler();
    LOGT(("ServiceStart. Begin initialize scheduler tasks\n"));
    pScheduler->Init();
    LOGT(("ServiceStart. Initialize scheduler tasks completed\n"));
    pScheduler->Start();
    LOGT(("ServiceStart. Starting scheduler tasks completed\n"));
    
    while (pScheduler->GetState() == STATE_STARTED)
    {
        Sleep(1000);
    }
    delete pScheduler;
    delete dbLayer;

    xsltCleanupGlobals();
    xmlCleanupParser();    
    curl_global_cleanup();
}

VOID ServiceStop()
{
    LOGT(("ServiceStart. Begin stopping scheduler tasks\n"));
    pScheduler->Stop();
    LOGT(("ServiceStart. Stopping scheduler tasks completed\n"));
    LOGT(("ServiceStart. W3M version 1.0 at %s %s stopped\n", __DATE__, __TIME__));
}

VOID CreateClientService(LPCTSTR szBinaryPathName)
{
    SC_HANDLE schService, schSCManager;
    printf("Open service manager... ");
    schSCManager = OpenSCManager(NULL, NULL, GENERIC_WRITE);
    if (!schSCManager)
    {
        printf("fail! Error code: 0x%08X\n", GetLastError());
        return;
    }
    printf("done\n");
    printf("Creating service... ");
    schService = CreateService(
                                schSCManager,              // SCManager database
                                SERVICENAME,               // name of service
                                SERVICEDISPLAYNAME,        // service name to display
                                SERVICE_ALL_ACCESS,        // desired access
                                SERVICE_WIN32_OWN_PROCESS, // service type
                                SERVICE_AUTO_START,        // start type
                                SERVICE_ERROR_NORMAL,      // error control type
                                szBinaryPathName,          // service's binary
                                NULL,                      // no load ordering group
                                NULL,                      // no tag identifier
                                NULL,                      // no dependencies
                                NULL,                      // LocalSystem account
                                NULL);                     // no password
    if (schService)
        printf("done\n");
    else
        printf("fail! Error code: 0x%08X\n", GetLastError());
    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
}

VOID DeleteClientService()
{
    SC_HANDLE schService, schSCManager;
    printf("Open service manager... ");
    schSCManager = OpenSCManager(NULL, NULL, GENERIC_WRITE);
    if (!schSCManager)
    {
        printf("fail! Error code: 0x%08X\n", GetLastError());
        return;
    }
    printf("done\n");
    printf("Open service... ");
    schService = OpenService(schSCManager, SERVICENAME, DELETE);
    if (!schService)
    {
        printf("fail! Error code: 0x%08X\n", GetLastError());
        CloseServiceHandle(schSCManager);
        return;
    }
    printf("done\n");
    printf("Deleting service... ");
    if (DeleteService(schService))
        printf("done\n");
    else
        printf("fail! Error code: 0x%08X\n", GetLastError());
    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
}